package com.yykj.common.service.impl;

import com.yykj.common.constant.RedisConstant;
import com.yykj.common.enums.ResultEnums;
import com.yykj.common.exception.NormalException;
import com.yykj.common.service.RedisService;
import com.yykj.common.util.GsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Created by chendg
 * on 2018/5/11 0011.
 */
@Service
@Slf4j
public class RedisServiceImpl implements RedisService {

    private StringRedisTemplate redisTemplate;


    @Value("${spring.profiles.active}")
    public String active;

    @Value("${spring.application.name}")
    public String applicationName;

    @Autowired
    public void setRedisTemplate(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public String getKeyLock(String key) {
        String lockValue;
        boolean lockStatus;
        int cnt = 0;
        do {
            cnt++;
            lockValue = "" + (System.currentTimeMillis() + 2000);
            lockStatus = this.lock(key, lockValue);
            if (!lockStatus) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        } while (cnt < 5 && !lockStatus);
        if (!lockStatus) {
            throw new NormalException(ResultEnums.REDIS_LOCK_ERROR);
        }
        return lockValue;
    }

    @Override
    public boolean lock(String key, String value) {
        key = formatKey(key);
        if (Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value))) {
            return true;
        }
        //currentValue=A   这两个线程的value都是B  其中一个线程拿到锁
        String currentValue = redisTemplate.opsForValue().get(key);

        log.info("【Redis Service 】 ,key = {}", key);
        log.info("【Redis Service 】 ,value = {}", value);
        log.info("【Redis Service 】 ,currentValue = {}", currentValue);
        //如果锁过期
        if (!StringUtils.isEmpty(currentValue)
                && Long.parseLong(currentValue) < System.currentTimeMillis()) {
            //获取上一个锁的时间
            String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
            if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
                return true;
            }
        }

        return false;
    }

    @Override
    public void unlock(String key, String value) {
        key = formatKey(key);
        try {
            String currentValue = redisTemplate.opsForValue().get(key);
            if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        } catch (Exception e) {
            log.error("【redis分布式锁】解锁异常, {}", e);
        }
    }

    @Override
    public String get(String key) {
        key = formatKey(key);
        return redisTemplate.opsForValue().get(key);
    }

    @Override
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(formatKey(key), value, RedisConstant.EXPIRE, TimeUnit.SECONDS);
    }

    @Override
    public void set(String key, String value, long seconds) {
        redisTemplate.opsForValue().set(formatKey(key), value, seconds, TimeUnit.SECONDS);
    }

    @Override
    public void expire(String key, long expire) {
        redisTemplate.expire(formatKey(key), expire, TimeUnit.SECONDS);
    }

    @Override
    public void reset(String key) {
        key = formatKey(key);
        String value = redisTemplate.opsForValue().get(key);
        redisTemplate.opsForValue().set(key, value, RedisConstant.TOKEN_EXPIRE, TimeUnit.SECONDS);
    }

    @Override
    public void removeAllKeysLike(String prefix) {
        String key = formatKey(prefix + "*");
        Set<String> keys = redisTemplate.keys(key);
        for (String str : keys) {
            redisTemplate.delete(str);
            log.info("redis Service】删除key = {}", str);
        }
    }

    @Override
    public void remove(String key) {
        key = formatKey(key);
        redisTemplate.delete(key);
    }

    public String formatKey(String key) {
        applicationName = "";
        return String.format(RedisConstant.TOKEN_PREFIX, active , applicationName , key);
    }



    @Override
    public void setPojo(String key, Object object, long expire) {
        String value = GsonUtil.toJson(object);
        redisTemplate.opsForValue().set(formatKey(key), value, expire, TimeUnit.SECONDS);
    }

    @Override
    public <T> T getPojo(String key, Class clazz) {
        String value = redisTemplate.opsForValue().get(formatKey(key));
        return (T) GsonUtil.fromJson(value, clazz);
    }

    @Override
    public void addZsetIncrementScore(String key, String valus, Integer score) {
        key = formatKey(key);
        redisTemplate.opsForZSet().incrementScore(key , valus , score);
    }

    @Override
    public Set<String> reverseRange(String key, long start, long end) {
        key = formatKey(key);
        Set<String> values = redisTemplate.opsForZSet().reverseRange(key, start, end);
        return values;
    }

    @Override
    public Set<ZSetOperations.TypedTuple<String>> rangeWithScores(String key, long start, long end) {
        key = formatKey(key);
        Set<ZSetOperations.TypedTuple<String>> withScores = redisTemplate.opsForZSet().rangeWithScores(key, 0, -1);
        return withScores;
    }
}
